//
//  SKBoardView.m
//  Sudoku
//
//  Created by Richard Hodgkins on 08/09/2012.
//  Copyright (c) 2012 Richard Hodgkins. All rights reserved.
//

#import "SKBoardView.h"

#import "SKBoardGridView.h"

#import "SKNumberChooserView.h"
#import "SKBoardConstants.h"
#import "SKUICellLabel.h"

#import "SKBoard.h"

#define BOARD_SCALE 1
#define BOARD_INSET_SCALE 0.98

#define BOARD_BACKGROUND_ALPHA 0.6

#define CELL_COLOR UIColorFromRGB(0x555555)
#define BOX_COLOR_PENDING UIColorFromRGB(0x000000)
#define BOX_COLOR_COMPLETE UIColorFromRGB(0x00FF00)
#define BOX_COLOR_FAILED UIColorFromRGB(0xFF0000)
#define CELL_NONEDITABLE_COLOR UIColorFromGrayscale(0.2, 0)

@interface SKBoardView ()

@property (nonatomic, strong) SKBoard *boardObject;
@property (nonatomic, assign) id<SKBoardStateDelegate> delegate;
@property (nonatomic, assign) SKBoardState state;
@property (nonatomic, assign) NSUInteger currentNumber;
@property (nonatomic, strong) NSArray* editableUiLabels;

@property (nonatomic, strong) SKNumberChooserView *numberChooser;
@property (nonatomic, strong) SKBoardGridView *gridView;
@property (nonatomic, strong) UIView *numberView;

@end

@implementation SKBoardView

- (id) initWithBoard:(SKBoard *) board andFrame:(CGRect) frame andDelegate:(id<SKBoardStateDelegate>)delegate
{
    self = [super initWithFrame:frame];
    DLog(@"%@", NSStringFromCGRect(self.frame));
    if (self) {
        // Initialization code
        self.backgroundColor = [UIColor clearColor];
        self.autoresizingMask = UIViewAutoresizingNone;
        
        self.boardObject = board;
        self.delegate = delegate;
        
        CGRect boardFrame = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.bounds.size.width);
        
        CGRect chooserFrame = CGRectMake(self.bounds.origin.x, self.bounds.origin.y + boardFrame.size.height, self.bounds.size.width, self.bounds.size.height - boardFrame.size.height);
        
        self.gridView = [[SKBoardGridView alloc] initWithFrame:boardFrame];
        self.numberChooser = [[SKNumberChooserView alloc] initWithFrame:chooserFrame andDelegate:self];
        self.numberView = [[UIView alloc] initWithFrame:boardFrame];
                
        NSArray *fixedCells = [self setupBoardWithFrame:self.gridView.boardFrame];
        [self.gridView setFixedCellViews:fixedCells];
        [self setNumbersHidden:YES];
        
        [self addSubview:self.gridView];
        [self addSubview:self.numberView];
        [self addSubview:self.numberChooser];
    }
    return self;
}

- (NSArray *) setupBoardWithFrame:(const CGRect) boardFrame
{
    SInt8 *layout = (SInt8 *) self.boardObject.layout.bytes;
    SInt8 *moves = (SInt8 *) self.boardObject.moves.bytes;
    
    const CGSize cellSize= CGSizeApplyAffineTransform(boardFrame.size, CGAffineTransformMakeScale(CELL_PER_ROW_RECP, CELL_PER_COL_RECP));
    
    CGRect cellRect = boardFrame;
    cellRect.size = cellSize;
    
    NSMutableArray *fixedUiLabels = [NSMutableArray arrayWithCapacity:TOTAL_CELLS>>1];
    NSMutableArray *editableUiLabels = [NSMutableArray arrayWithCapacity:TOTAL_CELLS>>1];
    
    for (uint i=0; i<(BOX_ROWS * CELL_ROWS); i++) {
        for (uint j=0; j<(BOX_COLS * CELL_COLS); j++) {
                        
            // Cell rect contains frame of UILabel
            SKUICellLabel *cellLabel;
            if (moves) {
                cellLabel = [[SKUICellLabel alloc] initWithFrame:CGRectIntegral(cellRect) andNumber:*(layout++) andPreviousMove:*(moves++) andDelegate:self];
            } else {
                cellLabel = [[SKUICellLabel alloc] initWithFrame:CGRectIntegral(cellRect) andNumber:*(layout++) andDelegate:self];
            }
            
            [self.numberView addSubview:cellLabel];
                                
            cellRect.origin.x += cellSize.width;
            
            if ([cellLabel isEditable]) {
                [editableUiLabels addObject:cellLabel];
            } else {
                [fixedUiLabels addObject:cellLabel];
            }
        }
        cellRect.origin.x = boardFrame.origin.x;
        cellRect.origin.y += cellSize.height;
    }
    
    self.editableUiLabels = editableUiLabels;
    
    return fixedUiLabels;
}

-(void) setNumbersHidden:(BOOL) hidden
{
    self.numberView.hidden = hidden;
}

- (void) setIsInPencilMode:(BOOL)isInPencilMode
{
    for (SKUICellLabel *cell in self.editableUiLabels) {
        [cell updatePencilMode:isInPencilMode];
    }
}

#pragma mark - number chooser delegate

- (void)numberWasSelected:(NSUInteger)number
{
    if (self.currentNumber != number) {
//        DLog(@"Chaned number to: %u", number);
        self.currentNumber = number;
    }
}

#pragma mark - cell delegate

- (void) didTapCell:(SKUICellLabel *)cell
{
    if (self.state != SKBoardStateDisable) {
        BOOL needsChecking;
        if ([self.delegate isInPencilMode]) {
            needsChecking = [cell updatePencilNumber:self.currentNumber];
        } else {
            needsChecking = [cell updateCurrentNumber:self.currentNumber];
        }
        if (needsChecking) {
            [self checkCells];
        }
    }
}

- (void) didResetCell:(id)cell
{
    [self checkCells];
}

#pragma mark - state methods

- (void)reset
{
    DLog(@"Board was reset");
    self.state = SKBoardStateDisable;
    BOOL cellsChanged = NO;
    for (SKUICellLabel *cell in self.editableUiLabels) {
        // Hide and reset all cells
        if ([cell reset]) {
            cellsChanged = YES;
        }
    }
    [self setNumbersHidden:YES];
    if (cellsChanged) {
        [self update];
    }
}

- (void)clearPencilCells
{
    DLog(@"Board was cleared of penciled cells");
    BOOL cellsChanged = NO;
    for (SKUICellLabel *cell in self.editableUiLabels) {
        // Clear pencil mode
        if ([cell reset]) {
            cellsChanged = YES;
        }
    }
    if (cellsChanged) {
        [self update];
    }
}

- (void)enable
{
    DLog(@"Board was enabled");
    self.state = SKBoardStatePending;
    [self setNumbersHidden:NO];
    if (self.boardObject.moves) {
        [self checkCells];
    }
    [self update];
}

- (void)disable
{
    DLog(@"Board was disabled");
    self.state = SKBoardStatePending;
    [self setNumbersHidden:YES];
    [self update];
}

- (void) checkCells
{
    BOOL notifyDelegate = NO;
    
    if (self.state == SKBoardStateDisable) {
        // We're not enable so ignore
    } else if ([self.delegate isInPencilMode]) {
        // If we're in pencil mode then we're not allowed to complete the board
        self.state = SKBoardStatePending;
    } else {
    
        BOOL allCorrect = YES;
        BOOL emptyCell = NO;
        for (SKUICellLabel *cell in self.editableUiLabels) {
            const SKBoardState cellState = [cell state];
            if (cellState != SKCellStateCorrect) {
                // Mark incorrect so far
                allCorrect = NO;
                if (cellState == SKCellStateEmpty) {
                    // and we've found an empty cell so we don't need to look for more
                    emptyCell = YES;
                    break;
                }
            }
        }

        if (emptyCell) {
            // Empty cells means pending game
            self.state = SKBoardStatePending;
        } else {
            if (allCorrect) {
                // Game complete
                self.state = SKBoardStateCompleted;
            } else {
                // Wrong cell
                self.state = SKBoardStateFailed;
            }
        }
        
        if (self.state == SKBoardStateCompleted) {
            DLog(@"Board was completed");
            // Notify delegate that the games complete
            notifyDelegate = YES;
        }
    }
    
    [self update];
    
    if (notifyDelegate) {
        // If we need to notify the delegate
        [self.delegate gameWasCompleted];
    }
}

- (void) update
{
    self.gridView.state = self.state;
}

- (void) saveStateToBoard
{
    if (self.state != SKBoardStatePending && self.state != SKBoardStateFailed) {
        // There's no point saving the game because it's either not started or complete
        return;
    }
    SInt8 *board = (SInt8 *) self.boardObject.layout.bytes;
        
    SInt8 *moves = (SInt8 *) malloc(TOTAL_CELLS * sizeof(SInt8));
    uint idx = 0;
    NSArray *cells = self.editableUiLabels;
    for (int i=0; i<TOTAL_CELLS; i++) {        
        if (board[i] < 0) {
            moves[i] = [[cells objectAtIndex:(idx++)] currentMove];
        } else {
            moves[i] = board[i];
        }
    }
    
    self.boardObject.moves = [NSData dataWithBytes:moves length:TOTAL_CELLS];
    
    free(moves);
}

@end
